[HVM] Save/restore: save pending interrupts/events on HVM vcpus
authorTim Deegan <Tim.Deegan@xensource.com>
Wed, 2 May 2007 13:41:51 +0000 (14:41 +0100)
committerTim Deegan <Tim.Deegan@xensource.com>
Wed, 2 May 2007 13:41:51 +0000 (14:41 +0100)
along with the rest of the cpu state.  This stops us from dropping
interrupts after the vlapic has recorded them in the ISR but before the
guest has actually taken them.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/hvm/vmx/vmx.c
xen/include/public/hvm/save.h

index d0afe8d6563b329103d59ca04dba25d44d972b8d..f74bdd9c3bf697a1e8663af58ac609cf2e46816a 100644 (file)
@@ -307,6 +307,41 @@ int svm_vmcb_save(struct vcpu *v, struct hvm_hw_cpu *c)
     c->sysenter_esp = vmcb->sysenter_esp;
     c->sysenter_eip = vmcb->sysenter_eip;
 
+    /* Save any event/interrupt that was being injected when we last
+     * exited.  Although there are three(!) VMCB fields that can contain
+     * active events, we only need to save at most one: because the
+     * intr_assist logic never delivers an IRQ when any other event is
+     * active, we know that the only possible collision is if we inject
+     * a fault while exitintinfo contains a valid event (the delivery of
+     * which caused the last exit).  In that case replaying just the
+     * first event should cause the same behaviour when we restore. */
+    if ( vmcb->vintr.fields.irq 
+         && /* Check it's not a fake interrupt (see svm_intr_assist()) */
+         !(vmcb->general1_intercepts & GENERAL1_INTERCEPT_VINTR) )
+    {
+        c->pending_vector = vmcb->vintr.fields.vector;
+        c->pending_type = 0; /* External interrupt */
+        c->pending_error_valid = 0;
+        c->pending_reserved = 0;
+        c->pending_valid = 1;
+        c->error_code = 0;
+    }
+    else if ( vmcb->exitintinfo.fields.v )
+    {
+        c->pending_event = vmcb->exitintinfo.bytes & 0xffffffff;
+        c->error_code = vmcb->exitintinfo.fields.errorcode;
+    }
+    else if ( vmcb->eventinj.fields.v ) 
+    {
+        c->pending_event = vmcb->eventinj.bytes & 0xffffffff;
+        c->error_code = vmcb->eventinj.fields.errorcode;
+    }
+    else 
+    {
+        c->pending_event = 0;
+        c->error_code = 0;
+    }
+
     return 1;
 }
 
@@ -335,7 +370,7 @@ int svm_vmcb_restore(struct vcpu *v, struct hvm_hw_cpu *c)
 
     if ( !svm_paging_enabled(v) ) 
     {
-        printk("%s: paging not enabled.", __func__);
+        printk("%s: paging not enabled.\n", __func__);
         goto skip_cr3;
     }
 
@@ -436,11 +471,33 @@ int svm_vmcb_restore(struct vcpu *v, struct hvm_hw_cpu *c)
     vmcb->dr6 = c->dr6;
     vmcb->dr7 = c->dr7;
 
+    if ( c->pending_valid ) 
+    {
+        gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",
+                 c->pending_event, c->error_code);
+
+        /* VMX uses a different type for #OF and #BP; fold into "Exception"  */
+        if ( c->pending_type == 6 ) 
+            c->pending_type = 3;
+        /* Sanity check */
+        if ( c->pending_type == 1 || c->pending_type > 4 
+             || c->pending_reserved != 0 )
+        {
+            gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n", 
+                     c->pending_event);
+            return -EINVAL;
+        }
+        /* Put this pending event in exitintinfo and svm_intr_assist()
+         * will reinject it when we return to the guest. */
+        vmcb->exitintinfo.bytes = c->pending_event;
+        vmcb->exitintinfo.fields.errorcode = c->error_code;
+    }
+
     paging_update_paging_modes(v);
     return 0;
  
  bad_cr3:
-    gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"", c->cr3);
+    gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"\n", c->cr3);
     return -EINVAL;
 }
 
index 1c47aadebcb5b6868c0b7b8fedcead269155b642..5cc2674c852aac79e635d9e9b8c8037e36c9c6e9 100644 (file)
@@ -370,6 +370,8 @@ static inline void __restore_debug_registers(struct vcpu *v)
 
 int vmx_vmcs_save(struct vcpu *v, struct hvm_hw_cpu *c)
 {    
+    uint32_t ev;
+
     c->rip = __vmread(GUEST_RIP);
     c->rsp = __vmread(GUEST_RSP);
     c->rflags = __vmread(GUEST_RFLAGS);
@@ -436,6 +438,28 @@ int vmx_vmcs_save(struct vcpu *v, struct hvm_hw_cpu *c)
     c->sysenter_esp = __vmread(GUEST_SYSENTER_ESP);
     c->sysenter_eip = __vmread(GUEST_SYSENTER_EIP);
 
+    /* Save any event/interrupt that was being injected when we last
+     * exited.  IDT_VECTORING_INFO_FIELD has priority, as anything in
+     * VM_ENTRY_INTR_INFO_FIELD is either a fault caused by the first
+     * event, which will happen the next time, or an interrupt, which we
+     * never inject when IDT_VECTORING_INFO_FIELD is valid.*/
+    if ( (ev = __vmread(IDT_VECTORING_INFO_FIELD)) & INTR_INFO_VALID_MASK ) 
+    {
+        c->pending_event = ev;
+        c->error_code = __vmread(IDT_VECTORING_ERROR_CODE);
+    }
+    else if ( (ev = __vmread(VM_ENTRY_INTR_INFO_FIELD)) 
+              & INTR_INFO_VALID_MASK ) 
+    {
+        c->pending_event = ev;
+        c->error_code = __vmread(VM_ENTRY_EXCEPTION_ERROR_CODE);
+    }
+    else 
+    {
+        c->pending_event = 0;
+        c->error_code = 0;
+    }
+
     return 1;
 }
 
@@ -563,6 +587,48 @@ int vmx_vmcs_restore(struct vcpu *v, struct hvm_hw_cpu *c)
     vmx_vmcs_exit(v);
 
     paging_update_paging_modes(v);
+
+    if ( c->pending_valid ) 
+    {
+        vmx_vmcs_enter(v);
+        gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",
+                 c->pending_event, c->error_code);
+
+        /* SVM uses type 3 ("Exception") for #OF and #BP; VMX uses type 6 */
+        if ( c->pending_type == 3 
+             && (c->pending_vector == 3 || c->pending_vector == 4) ) 
+            c->pending_type = 6;
+
+        /* For software exceptions, we need to tell the hardware the 
+         * instruction length as well (hmmm). */
+        if ( c->pending_type > 4 ) 
+        {
+            int addrbytes, ilen; 
+            if ( (c->cs_arbytes & (1u<<13)) && (c->msr_efer & EFER_LMA) ) 
+                addrbytes = 8;
+            else if ( (c->cs_arbytes & (1u<<14)) ) 
+                addrbytes = 4;
+            else 
+                addrbytes = 2;
+            ilen = hvm_instruction_length(c->rip, hvm_guest_x86_mode(v));
+            __vmwrite(VM_ENTRY_INSTRUCTION_LEN, ilen);
+        }
+
+        /* Sanity check */
+        if ( c->pending_type == 1 || c->pending_type > 6
+             || c->pending_reserved != 0 )
+        {
+            gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n", 
+                     c->pending_event);
+            return -EINVAL;
+        }
+        /* Re-inject the exception */
+        __vmwrite(VM_ENTRY_INTR_INFO_FIELD, c->pending_event);
+        __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, c->error_code);
+        v->arch.hvm_vmx.vector_injected = 1;
+        vmx_vmcs_exit(v);
+    }
+
     return 0;
 
  bad_cr3:
index effa3e39bac86a8321a3f81a665e83936a0d9308..e01218a9d6f47cd1b034b7719f2287d35e9cee04 100644 (file)
@@ -182,6 +182,20 @@ struct hvm_hw_cpu {
 
     /* guest's idea of what rdtsc() would return */
     uint64_t tsc;
+
+    /* pending event, if any */
+    union {
+        uint32_t pending_event;
+        struct {
+            uint8_t  pending_vector:8;
+            uint8_t  pending_type:3;
+            uint8_t  pending_error_valid:1;
+            uint32_t pending_reserved:19;
+            uint8_t  pending_valid:1;
+        };
+    };
+    /* error code for pending event */
+    uint32_t error_code;
 };
 
 DECLARE_HVM_SAVE_TYPE(CPU, 2, struct hvm_hw_cpu);